using System;
using NHibernate;

namespace FwkLight.Persistence
{
  public class NHibernateUnitOfWork : INHUnitOfWork
  {
    private readonly ISessionSource _source;
    private bool _isDisposed;
    private bool _isInitialized;
    private ITransaction _transaction;

    public NHibernateUnitOfWork(ISessionSource source)
    {
      _source = source;
    }

    #region INHUnitOfWork Members

    public void Initialize()
    {
      should_not_currently_be_disposed();

      if (_isInitialized)
        return;

      Session = _source.CreateSession();

      _isInitialized = true;
    }

    public void BeginTransaction()
    {
      should_be_initialized_first();
      should_not_currently_be_disposed();

      begin_new_transaction();
    }

    public ISession Session { get; private set; }

    public void Commit()
    {
      should_not_currently_be_disposed();
      should_be_initialized_first();

      _transaction.Commit();

      TransactionInProgress = false;
    }

    public void Rollback()
    {
      should_not_currently_be_disposed();
      should_be_initialized_first();

      _transaction.Rollback();

      TransactionInProgress = false;
    }

    public bool TransactionInProgress { get; set;}

    public void Dispose()
    {
      if (_isDisposed || !_isInitialized) return;

      if (_transaction != null)
        _transaction.Dispose();

      Session.Dispose();

      _isDisposed = true;

      TransactionInProgress = false;
    }

    #endregion

    private void begin_new_transaction()
    {
      if (_transaction != null)
      {
        _transaction.Dispose();
      }

      _transaction = Session.BeginTransaction();

      TransactionInProgress = true;
    }

    private void should_not_currently_be_disposed()
    {
      if (_isDisposed) throw new ObjectDisposedException(GetType().Name);
    }

    private void should_be_initialized_first()
    {
      if (!_isInitialized)
        throw new InvalidOperationException(
          "Must initialize (call Initialize()) on NHibernateUnitOfWork before commiting or rolling back");
    }
  }
}